/*
 * sender.c
 *
 *  Created on: 2013. 10. 16.
 *      Author: jphong
 */

#include "np.h"
#include "packet.h"

typedef enum {
	Wait_for_call, Wait_for_ACK, Closing
} State;

extern int DEBUG_ON;	// print debug messages if set
static unsigned int ReTxTimeout = 100;		// default timeout in msec
static char *localhost = "127.0.0.1";			// default receiver hostname
static char *port = "5678";						// default receiver port

//Packet buffers
static Packet sndPacket, rcvPacket;
/*
 * rdt3.0 sender
 *
 */

int main(int argc, char **argv) {
	struct sockaddr_in peer;
	int fd, s,f=0;
	unsigned char sndSeq;
	Event event;
	State state;
	Packet *sndpkt = &sndPacket;
	Packet *rcvpkt = &rcvPacket;
	char *data = &(sndPacket.data[0]);
	char *hostname, *service;
	int opt;

	// Command line option processing
	while ((opt = getopt(argc, argv, "dt:")) != -1) {
		switch (opt) {
		case 'd':
			DEBUG_ON = 1; 	// Debug mode
			break;
		case 't':
			ReTxTimeout = atoi(optarg);
			break;
		default:
			error(1, 0,
					"Usage: %s [-d] [-t timeout] [<receiver name or IP address> <port number>]\n",
					argv[0]);
		}
	}
	if (optind == argc - 2) {
		hostname = argv[optind];
		service = argv[optind + 1];
	} else if (optind == argc) {
		hostname = localhost;
		service = port;
	} else
		error(1, 0,
				"Usage: %s [-d] [-t timeout] [<receiver name or IP address> <port number>]\n",
				argv[0]);
	debug("To %s:%s\tTimeout = %dmsec\tDebugMode = %s\n", hostname, service,
			ReTxTimeout, DEBUG_ON ? "on" : "off");

	set_address(hostname, service, &peer, "udp"); // Set peer's IP and port

	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
		error(1, errno, "socket creation failed\n");


	fd = fileno(stdin);

	// Init conditiion
	sndSeq = 0;
	state = Wait_for_call;

	// FSM
	for (;;) {
		int datalen, rcvlen;


		event = getEvent(fd, s);
		// Actions taken by State and Event
		switch (state) {
		// Wait_for_call state
		case Wait_for_call:
			switch (event) {
			case RDT_SEND:
				if (fgets(data, MAX_DATA, stdin) != NULL ) { // Get data from above
					//if ( fgets( data, MAX_DATA, file) != NULL ) {  // Get data from above
					/*
					 * More codes here ...
					 */
					sndpkt = make_pkt(DATA, sndSeq, &sndPacket, MAX_DATA);
					udt_send(s, sndpkt, (struct sockaddr *) &peer, sizeof(peer));
					start_timer(ReTxTimeout);
					state = Wait_for_ACK;
					continue;
				} else {				// EoF encountered. Close the session
					/*
					 * More codes here ...
					 */
					state = Closing;
				}
			default:
				continue;				// ignore the event
			}
			//Wait_for_ACK state
		case Wait_for_ACK:
			switch (event) {
			case RDT_RCV:
				/*
				 * More codes here ...
				 */
				recvfrom(s, rcvpkt, sizeof(rcvpkt), 0,(struct sockaddr *)&peer, sizeof(peer));
				if (isType(rcvpkt, ACK, sndSeq)) {
					stop_timer();
					sndSeq = (sndSeq + 1) % 2;
					state = Wait_for_call;
				}
				continue;
			case TIMEOUT:
				/*
				 * More codes here ...
				 */
				udt_send(s, sndpkt, (struct sockaddr *) &peer, sizeof(peer));
				start_timer(ReTxTimeout);
				continue;
			default:
				continue;
			}
			// Closing state
			// wait an ACK. Sender shall stop when anything hear from receiver
		case Closing:
			switch (event) {
			case RDT_RCV:
				/*
				 * More codes here ...
				 */
				sndpkt = make_pkt(END, sndSeq, &sndPacket, 0);
				udt_send(s, sndpkt, (struct sockaddr *) &peer, sizeof(peer));
				sndSeq = (sndSeq + 1) % 2;
				start_timer(ReTxTimeout);
				if(rcvpkt, ACK, sndpkt){
					f = -10;
					break;
				}
			case TIMEOUT:
				/*
				 * More codes here ...
				 */
				udt_send(s, sndpkt, (struct sockaddr *) &peer, sizeof(peer));
				start_timer(ReTxTimeout);
				continue;
			default:
				continue;
			}	// End of inner switch
			break;
		}	// End of outer switch
		break;
if(f == -10)break;	}   // End of for loop

}	// End of main

